package org.study.springboot.common.Utils;

import com.jcraft.jsch.*;
import org.apache.commons.lang3.StringUtils;
import org.study.springboot.model.sftp.SftpConfigBO;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ConcurrentHashMap;

public class SftpUtils {
    private static ConcurrentHashMap<String, ChannelSftp> channelSftphMap = new ConcurrentHashMap<>();

    /**
     * 错误次数
     */
    private static final int ERROR_COUNT = 3;

    private static void close(ChannelSftp channelSftp) {
        try {
            if (channelSftp != null) {
                Session session = null;
                session = channelSftp.getSession();
                session.disconnect();
                channelSftp.disconnect();
            }
        } catch (JSchException e) {
            e.printStackTrace();
        }
    }

    public static ChannelSftp sftpLogin(String host, int port, String username, String password, String privateKey, String type) {
        int count = 0;
        return retrySftpLogin(host, port, username, password, privateKey, count, type);
    }


    public static ChannelSftp retrySftpLogin(String host,int port,String username, String password, String privateKey, int count,String type) {
        if (channelSftphMap.containsKey(type)) {
            ChannelSftp channelSftp = channelSftphMap.get(type);
            if (channelSftp != null && channelSftp.isConnected()) {
                return channelSftp;
            } else {
                close(channelSftp);
                channelSftphMap.remove(privateKey);
            }
        }
        JSch jSch = new JSch();
        if (StringUtils.isNoneEmpty(privateKey)) {
            try {
                jSch.addIdentity(privateKey);
            } catch (JSchException e) {
                e.printStackTrace();
            }
        }
        ChannelSftp channelSftp = null;
        try {
            Session session = jSch.getSession(username, host, port);
            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect();
            channelSftp = (ChannelSftp)session.openChannel("sftp");
            channelSftp.connect();
            channelSftphMap.put(type, channelSftp);
            return channelSftp;
        } catch (JSchException e) {
            count++;
           return retrySftpLogin(host, port, username, password, privateKey, count, type);
        }
    }

    public static byte[] download(SftpConfigBO sftpConfigBO, String remotePath, String fileName, String type)  {

        String fs = System.getProperties().getProperty("file.separator");
        ChannelSftp channelSftp = sftpLogin(sftpConfigBO.getHost(), sftpConfigBO.getPort(), sftpConfigBO.getUsername(), sftpConfigBO.getPassword(), sftpConfigBO.getPrivateKey(), sftpConfigBO.getType());
        String path = remotePath + fs + fileName;

        if (!isExist(path, channelSftp)) {
            throw new RuntimeException("获取报告异常");
        }

        byte[] bytes  = null;

        try {
            channelSftp.cd(remotePath);
            bytes = remoteFile2ByteArray(channelSftp, fileName);
            return bytes;
        } catch (Exception e) {
            throw new RuntimeException("获取报告异常");
        }
    }

    public static boolean upload(SftpConfigBO sftpConfigBO, String remotePath, String fileName, byte[] data, String type) {
        ChannelSftp channelSftp = sftpLogin(sftpConfigBO.getHost(), sftpConfigBO.getPort(), sftpConfigBO.getUsername(), sftpConfigBO.getPassword(), sftpConfigBO.getPrivateKey(), sftpConfigBO.getType());

        InputStream inputStream = null;
        try {
            inputStream = new ByteArrayInputStream(data);

            StringBuilder sb = new StringBuilder();
            if (StringUtils.isNotEmpty(sftpConfigBO.getDirectory())) {
                sb.append("/").append(sftpConfigBO.getDirectory());
            }
            sb.append(remotePath);
            channelSftp.cd("/");
            channelSftp.cd(channelSftp.getHome());

            createDir(sb.toString(), channelSftp);

            channelSftp.put(inputStream, fileName);

            channelSftp.cd("/");
            return true;
        } catch (Exception e) {
            return false;
        }finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void createDir(String path, ChannelSftp channelSftp) throws SftpException {
        if (isDirExist(path, channelSftp)) {
            channelSftp.cd(path);
        }else {
            String[] pathArray = path.split("/");
            for (String s : pathArray) {
                if (StringUtils.isEmpty(s)){
                    continue;
                }
                if (isDirExist(s, channelSftp)) {
                    channelSftp.cd(path);
                }else {
                    channelSftp.mkdir(s);
                    channelSftp.cd(s);
                }
            }
        }
    }

    private static boolean isDirExist(String path, ChannelSftp channelSftp) {

        try {
            SftpATTRS lstat = channelSftp.lstat(path);
            return lstat.isDir();
        } catch (SftpException e) {
            return false;
        }
    }




    public static byte[]remoteFile2ByteArray(ChannelSftp channelSftp, String fileName) throws Exception {

        InputStream is = null;
        try {
            is = channelSftp.get(fileName);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024 * 8];
            int read = 0;
            while ((read = is.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
            return out.toByteArray();
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public static boolean isExist(String path, ChannelSftp channelSftp) {
        try {
            channelSftp.lstat(path);
            return true;
        } catch (SftpException e) {
            return false;
        }
    }



}
